using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Compact;
using Serilog.Sinks.SystemConsole.Themes;
using System.Diagnostics;

namespace CoreManager.Core.Logging
{
    /// <summary>
    /// Serilog配置
    /// </summary>
    public static class SerilogConfiguration
    {
        /// <summary>
        /// 配置Serilog
        /// </summary>
        /// <param name="configuration">配置</param>
        /// <param name="environment">环境</param>
        /// <returns></returns>
        public static LoggerConfiguration ConfigureSerilog(IConfiguration configuration, IHostEnvironment environment)
        {
            var loggerConfig = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .Enrich.WithProperty("Application", "CoreManager")
                .Enrich.WithProperty("Environment", environment.EnvironmentName)
                .Enrich.WithProperty("MachineName", Environment.MachineName)
                .Enrich.WithProperty("ProcessId", Environment.ProcessId);

            // 根据环境配置不同的输出
            if (environment.IsDevelopment())
            {
                ConfigureDevelopmentLogging(loggerConfig);
            }
            else if (environment.IsStaging())
            {
                ConfigureStagingLogging(loggerConfig, configuration);
            }
            else if (environment.IsProduction())
            {
                ConfigureProductionLogging(loggerConfig, configuration);
            }

            return loggerConfig;
        }

        /// <summary>
        /// 配置开发环境日志
        /// </summary>
        private static void ConfigureDevelopmentLogging(LoggerConfiguration loggerConfig)
        {
            loggerConfig
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Warning)
                .WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
                    theme: AnsiConsoleTheme.Code)
                .WriteTo.File(
                    path: "logs/CoreManager-dev-.log",
                    rollingInterval: RollingInterval.Day,
                    retainedFileCountLimit: 7,
                    outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}");
        }

        /// <summary>
        /// 配置测试环境日志
        /// </summary>
        private static void ConfigureStagingLogging(LoggerConfiguration loggerConfig, IConfiguration configuration)
        {
            loggerConfig
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
                .WriteTo.File(
                    formatter: new CompactJsonFormatter(),
                    path: "logs/CoreManager-staging-.json",
                    rollingInterval: RollingInterval.Day,
                    retainedFileCountLimit: 30);

            // 如果配置了Elasticsearch，添加Elasticsearch sink
            var elasticsearchUrl = configuration.GetConnectionString("Elasticsearch");
            if (!string.IsNullOrEmpty(elasticsearchUrl))
            {
                loggerConfig.WriteTo.Elasticsearch(new Serilog.Sinks.Elasticsearch.ElasticsearchSinkOptions(new Uri(elasticsearchUrl))
                {
                    IndexFormat = "CoreManager-logs-{0:yyyy.MM.dd}",
                    AutoRegisterTemplate = true,
                    AutoRegisterTemplateVersion = Serilog.Sinks.Elasticsearch.AutoRegisterTemplateVersion.ESv7
                });
            }
        }

        /// <summary>
        /// 配置生产环境日志
        /// </summary>
        private static void ConfigureProductionLogging(LoggerConfiguration loggerConfig, IConfiguration configuration)
        {
            loggerConfig
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("System", LogEventLevel.Error)
                .WriteTo.Console(
                    formatter: new CompactJsonFormatter())
                .WriteTo.File(
                    formatter: new CompactJsonFormatter(),
                    path: "logs/CoreManager-.json",
                    rollingInterval: RollingInterval.Day,
                    retainedFileCountLimit: 90,
                    fileSizeLimitBytes: 100 * 1024 * 1024, // 100MB
                    rollOnFileSizeLimit: true);

            // 生产环境可能需要发送到集中式日志系统
            var elasticsearchUrl = configuration.GetConnectionString("Elasticsearch");
            if (!string.IsNullOrEmpty(elasticsearchUrl))
            {
                loggerConfig.WriteTo.Elasticsearch(new Serilog.Sinks.Elasticsearch.ElasticsearchSinkOptions(new Uri(elasticsearchUrl))
                {
                    IndexFormat = "CoreManager-prod-{0:yyyy.MM.dd}",
                    AutoRegisterTemplate = true,
                    AutoRegisterTemplateVersion = Serilog.Sinks.Elasticsearch.AutoRegisterTemplateVersion.ESv7,
                    FailureCallback = e => Console.WriteLine($"Unable to submit event {e.MessageTemplate}"),
                    EmitEventFailure = Serilog.Sinks.Elasticsearch.EmitEventFailureHandling.WriteToSelfLog |
                                      Serilog.Sinks.Elasticsearch.EmitEventFailureHandling.WriteToFailureSink
                });
            }

            // 错误级别的日志发送邮件通知（如果配置了）
            var smtpServer = configuration["Email:SmtpServer"];
            if (!string.IsNullOrEmpty(smtpServer))
            {
                            loggerConfig.WriteTo.Email(
                fromEmail: configuration["Email:FromEmail"] ?? "noreply@CoreManager.com",
                toEmail: configuration["Email:AlertEmail"] ?? "admin@CoreManager.com",
                mailServer: smtpServer,
                restrictedToMinimumLevel: LogEventLevel.Error,
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
                mailSubject: "CoreManager Production Error Alert");
            }
        }
    }

    /// <summary>
    /// Serilog扩展
    /// </summary>
    public static class SerilogExtensions
    {
        /// <summary>
        /// 添加关联ID到日志
        /// </summary>
        public static LoggerConfiguration WithCorrelationIdHeader(this Serilog.Configuration.LoggerEnrichmentConfiguration enrichmentConfiguration)
        {
            return enrichmentConfiguration.With<CorrelationIdEnricher>();
        }

        /// <summary>
        /// 添加请求ID到日志
        /// </summary>
        public static LoggerConfiguration WithRequestId(this Serilog.Configuration.LoggerEnrichmentConfiguration enrichmentConfiguration)
        {
            return enrichmentConfiguration.With<RequestIdEnricher>();
        }
    }

    /// <summary>
    /// 关联ID日志增强器
    /// </summary>
    public class CorrelationIdEnricher : Serilog.Core.ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, Serilog.Core.ILogEventPropertyFactory propertyFactory)
        {
            var correlationId = GetCorrelationId();
            if (!string.IsNullOrEmpty(correlationId))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("CorrelationId", correlationId));
            }
        }

        private static string? GetCorrelationId()
        {
            // 从当前Activity获取关联ID，如果没有则返回null
            var activity = Activity.Current;
            return activity?.GetTagItem("correlation.id")?.ToString() ?? null;
        }
    }

    /// <summary>
    /// 请求ID日志增强器
    /// </summary>
    public class RequestIdEnricher : Serilog.Core.ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, Serilog.Core.ILogEventPropertyFactory propertyFactory)
        {
            var requestId = GetRequestId();
            if (!string.IsNullOrEmpty(requestId))
            {
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestId", requestId));
            }
        }

        private static string? GetRequestId()
        {
            // 从当前Activity获取请求ID，如果没有则返回null
            var activity = Activity.Current;
            return activity?.GetTagItem("request.id")?.ToString() ?? null;
        }
    }
}